1 module hip.timer; 2 public import hip.api.systems.timer; 3 4 class HipTimer : IHipTimer 5 { 6 ///Enforce naming for making debugging easier 7 string name; 8 protected uint loopCount = 0; 9 protected bool loops = false; 10 protected float deltaTime = 0; 11 protected float accumulator = 0; 12 protected float durationSeconds = 0; 13 protected bool isRunning = false; 14 protected void delegate(float progress, uint loopCount)[] handlers; 15 protected HipTimerType type; 16 17 /** 18 * Call `addHandler` after creation for adding what to do 19 */ 20 this(string name, float durationSeconds, HipTimerType type = HipTimerType.oneShot, bool loops = false) 21 { 22 this.setProperties(name, durationSeconds, type, loops); 23 } 24 /** 25 * Perfect function for making a timer pool 26 */ 27 void setProperties(string name, float durationSeconds, HipTimerType type, bool loops) 28 { 29 this.name = name; 30 this.durationSeconds = durationSeconds; 31 this.type = type; 32 this.loops = loops; 33 assert(type == HipTimerType.oneShot || type == HipTimerType.progressive, "Invalid timer type"); 34 isRunning = false; 35 accumulator = 0; 36 loopCount = 0; 37 } 38 string getName(){return name;} 39 float getDuration(){return durationSeconds;} 40 float getProgress() 41 { 42 if(durationSeconds == 0 || accumulator >= durationSeconds) 43 return 1.0; 44 return accumulator/durationSeconds; 45 } 46 HipTimer addHandler(void delegate() handler) 47 { 48 handlers~=(prog, count){handler();}; 49 return this; 50 } 51 HipTimer addHandler(void delegate(float progress) handler) 52 { 53 handlers~=(prog, count){handler(prog);}; 54 return this; 55 } 56 HipTimer addHandler(void delegate(float progress, uint loopCount) handler) 57 { 58 handlers~=handler; 59 return this; 60 } 61 void forceFinish() 62 { 63 foreach (h; handlers) h(1, loopCount); 64 stop(); 65 } 66 67 void pause(){isRunning = false;} 68 HipTimer play() 69 { 70 if(durationSeconds == 0) 71 forceFinish(); 72 else 73 isRunning = true; 74 return this; 75 } 76 void stop() 77 { 78 isRunning = false; 79 accumulator = 0; 80 loopCount = 0; 81 } 82 void reset() 83 { 84 stop(); 85 play(); 86 } 87 void loopRestart() 88 { 89 loopCount++; 90 accumulator = 0; 91 } 92 93 private void executeHandlers() 94 { 95 foreach(h;handlers)h(getProgress(), loopCount); 96 } 97 private bool checkLoops() 98 { 99 if(loops) 100 { 101 loopRestart(); 102 return false; 103 } 104 stop(); 105 return true; 106 } 107 108 109 ///Returns wether it has finished 110 bool tick (float dt) 111 { 112 if(isRunning) 113 { 114 this.deltaTime = dt; 115 accumulator+= dt; 116 switch(type) 117 { 118 case HipTimerType.oneShot: 119 if(accumulator >= durationSeconds) 120 { 121 executeHandlers(); 122 return checkLoops(); 123 } 124 break; 125 case HipTimerType.progressive: 126 executeHandlers(); 127 if(accumulator >= durationSeconds) 128 return checkLoops(); 129 break; 130 default:break; 131 } 132 } 133 return false; 134 } 135 } 136 137 class HipSequence : HipTimer, IHipTimerList 138 { 139 protected IHipTimer[] timerList; 140 protected uint listCursor = 0; 141 protected float cursorDuration = 0; 142 protected float listAccumulator = 0; 143 protected void delegate()[] onFinishList; 144 145 this(string name = "Sequence", scope IHipTimer[] timers = []...) 146 { 147 super(name, 0, HipTimerType.progressive); 148 foreach(t;timers) 149 { 150 timerList~= t; 151 } 152 cursorDuration = timerList[0].getDuration(); 153 recalculateDuration(); 154 155 addHandler((prog, count) 156 { 157 if(accumulator - listAccumulator >= cursorDuration) 158 { 159 if(listCursor + 1 < timerList.length) 160 { 161 //Guarantee that it finishes here 162 timerList[listCursor].tick(deltaTime); 163 cursorDuration = timerList[++listCursor].getDuration(); 164 timerList[listCursor].play(); 165 listAccumulator+= cursorDuration; 166 } 167 } 168 timerList[listCursor].tick(deltaTime); 169 }); 170 } 171 override HipSequence play() 172 { 173 super.play(); 174 timerList[0].play(); 175 return this; 176 } 177 override void stop() 178 { 179 super.stop(); 180 foreach(onFinish; onFinishList) 181 onFinish(); 182 } 183 184 void recalculateDuration() 185 { 186 foreach(t;timerList) 187 durationSeconds+= t.getDuration(); 188 setProperties(this.name, durationSeconds, HipTimerType.progressive, false); 189 } 190 HipSequence add(IHipTimer timer) 191 { 192 if(cursorDuration == 0) 193 cursorDuration = timer.getDuration(); 194 timerList~= timer; 195 recalculateDuration(); 196 return this; 197 } 198 HipSequence addOnFinish(void delegate() onFinish) 199 { 200 onFinishList~= onFinish; 201 return this; 202 } 203 } 204 205 class HipSpawn : HipTimer, IHipTimerList 206 { 207 protected IHipTimer[] timerList; 208 protected void delegate()[] onFinishList; 209 210 /** 211 * Will receive a list of timers and update them at the same time. 212 * Total duration is equal to the max timer duration. 213 * Call `.play()` after `new HipSpawn`, it is necessary for the timers 214 * to be updated. 215 */ 216 this(string name = "Spawn", scope IHipTimer[] timers = []...) 217 { 218 super(name, 0, HipTimerType.progressive); 219 foreach(t;timers) 220 { 221 timerList~= t; 222 } 223 recalculateDuration(); 224 addHandler((prog, count) 225 { 226 foreach(t; timerList) 227 t.tick(deltaTime); 228 }); 229 } 230 override HipSpawn play() 231 { 232 super.play(); 233 foreach(t; timerList) 234 { 235 t.play(); 236 } 237 return this; 238 } 239 protected void recalculateDuration() 240 { 241 float durationSeconds = 0; 242 foreach(t;timerList) 243 { 244 if(t.getDuration() > durationSeconds) 245 durationSeconds = t.getDuration(); 246 } 247 setProperties(this.name, durationSeconds, HipTimerType.progressive, false); 248 } 249 250 override void stop() 251 { 252 super.stop(); 253 foreach(onFinish; onFinishList) 254 onFinish(); 255 } 256 257 HipSpawn add(IHipTimer timer) 258 { 259 timerList~= timer; 260 recalculateDuration(); 261 return this; 262 } 263 264 HipSpawn addOnFinish(void delegate() onFinish) 265 { 266 onFinishList~= onFinish; 267 return this; 268 } 269 }